import pygame
from pygame.locals import *

import G
import Config
import Vector
import Util
import Timekeeper

explosion_mid = Config.explosion_mid
explosion_inner = Config.explosion_inner
edt = Config.explode_damage_thresholds
ett = Config.explode_temperature_thresholds
explosioncolors = Config.explosioncolors
explosion_outer_damage_mult = Config.explosion_outer_damage_mult
explosion_mid_damage_mult = Config.explosion_mid_damage_mult
damage_to_impulse = Config.damage_to_impulse
friendly_explosion_damage = Config.friendly_explosion_damage

def color(temperature):
   index = 0
   while index < len(ett) and ett[index] < temperature:
      index += 1
   if index == len(ett):
      return explosioncolors[-1]
   elif index == 0:
      return explosioncolors[0]
   else:
      color = explosioncolors[index-1]
      colornext = explosioncolors[index]
      r = (temperature - ett[index-1]) * 1.0 / (ett[index] - ett[index-1])
      return [int(color[i] + r * (colornext[i] - color[i])) for i in range(0,3)]

def explode(center, damage, radius, lifetime, source=None):
   temp = temperature(damage)
   exp = Explosion(center, damage, temp, radius, lifetime)
   G.effects.add(exp)
   
   if source is not None and not friendly_explosion_damage:
      if source.friendly:
         groups = G.enemy_ships.sprites()
      else:
         groups = G.friendly_ships.sprites()
   else:
      groups = G.friendly_ships.sprites() + G.enemy_ships.sprites()
   impulse = damage * damage_to_impulse
   for sprite in groups:
      if sprite is not source or friendly_explosion_damage:
         to = Vector.v_sub(sprite.rect.center, center)
         dist = Vector.length(to)
         rr = Util.rectradius(sprite.rect)
         if dist <= rr + radius:
            if dist <= rr + radius * explosion_inner:
               sprite.damage(damage, 'explosion', source)
               sprite.impulse(Vector.set_length(to, impulse))
            elif dist <= rr + radius * explosion_mid:
               sprite.damage(damage * explosion_mid_damage_mult, 'explosion', source)
               sprite.impulse(Vector.set_length(to, impulse * explosion_mid_damage_mult))
            else:
               sprite.damage(damage * explosion_outer_damage_mult, 'explosion', source)
               sprite.impulse(Vector.set_length(to, impulse * explosion_outer_damage_mult))

def hitscan_trail(start, end, damage, dam_type, color=(255,255,255), 
                  hits=1, source=None, screen=None):
   if source.friendly:
      group = G.enemy_ships.sprites()
   else:
      group = G.friendly_ships.sprites()
   dummy = pygame.sprite.Sprite()
   if start[0] < end[0]:
      left = start[0]
      right = end[0]
   else:
      left = end[0]
      right = start[0]
   if start[1] < end[1]:
      top = start[1]
      bottom = end[1]
   else:
      top = end[1]
      bottom = start[1]
   dummy.rect = pygame.Rect(left, top, right-left, bottom-top)
   collisions = list()
   hit = False
   col_pre = pygame.sprite.spritecollide(dummy, group, False)
   if len(col_pre) > 0:
      for sprite in col_pre:
         intersection = Vector.rect_line_collide(start, end, sprite.rect)
         if intersection is not None:
            collisions.append((Vector.distance(start, sprite.rect.center), sprite, intersection))
      if len(collisions) > 0:
         collisions.sort(key=lambda x: x[0])
         if hits != -1:
            maxindex = min(hits, len(collisions))
         else:
            maxindex = len(collisions)
         numhit = 0
         for i in range(0, maxindex):
            collisions[i][1].damage(damage, dam_type, source)
            numhit += 1
         if numhit == hits:
            hit = True
            G.effects.add(Trail(start, collisions[-1][2], color, screen=screen))
   if not hit:
      G.effects.add(Trail(start, end, color, screen=screen))

def temperature(damage):
   index = 0
   while index < len(edt) and edt[index] < damage:
      index += 1
   if index == len(edt): #Massive damage!
      return ett[-1]
   elif index == 0:
      return ett[0]
   else:
      temp = ett[index-1]
      tempnext = ett[index]
      r = (damage - edt[index-1]) * 1.0 / (edt[index] - edt[index-1])
      return int(temp + r * (tempnext - temp))

class Animation(Timekeeper.Timekeeper):
   __slots__ = ('screen',)

   def __init__(self, screen=None):
      if screen is None:
         self.screen = G.game.screen
      else:
         self.screen = screen

   def update(self):
      return True

class Explosion(Animation):
   __slots__ = ('center',
                'temperature',
                'radius',
                'lifetime',
                'expand_rate',
                'expand_buffer',
                'damage',
                'rect',
                'image',
                'inner_color',
                'outer_color',
                'middle_color',
                'age',
                'topleft',
                'colliding',
                'praxrect')
  
   def __init__(self, center, damage, temperature, radius, lifetime, *args, **kwargs):
      Animation.__init__(self, *args, **kwargs)
      self.rect = pygame.Rect(center[0], center[1], 0, 0)
      self.praxrect = pygame.Rect(3*radius/2, radius, 0, 0)
      self.image = pygame.Surface((3*radius, 2*radius))
      self.image.set_colorkey((0,0,0), RLEACCEL)
      self.damage = damage
      self.temperature = temperature
      self.inner_color = color(temperature)
      self.middle_color = color(temperature*3/4)
      self.outer_color = color(temperature/2)
      self.radius = radius
      self.lifetime = lifetime
      self.expand_rate = self.radius * 1.0 / self.lifetime
      self.expand_buffer = 0.0
      self.age = 0.0
      self.topleft = (center[0] - radius*3/2, center[1] - radius)
      self.colliding = list()
   
   def update(self):
      frame_time = self.frame()
      self.age += frame_time
      self.expand_buffer += self.expand_rate * frame_time
      expansion, self.expand_buffer = divmod(self.expand_buffer, 1.0)
      expansion = int(expansion)
      self.rect.inflate_ip(2*expansion, 2*expansion)
      self.image.fill((0,0,0))
      pygame.draw.circle(self.image, self.outer_color, 
                         (self.radius*3/2, self.radius), self.rect.width/2)
      pygame.draw.circle(self.image, self.middle_color, 
                         (self.radius*3/2, self.radius), int(self.rect.width/2 * explosion_mid))
      pygame.draw.circle(self.image, self.inner_color, 
                         (self.radius*3/2, self.radius), int(self.rect.width/2 * explosion_inner))
      if Config.praxiseffect:
         self.praxrect.inflate_ip(3*expansion, 3*expansion/2)
         pygame.draw.arc(self.image, (255,255,255), self.praxrect, 
            2.3, 7.1, self.praxrect.width/40)
      if self.age > self.lifetime / 2:
         self.image.set_alpha(int(255 * (2 - 2 * self.age / self.lifetime)))
      self.screen.blit(self.image, self.topleft)
      if self.age > self.lifetime:
         return True
      else:
         return False

class Trail(Animation):
   __slots__ = ('start',
                'end',
                'color',
                'lifetime',
                'age')
   
   def __init__(self, start, end, color=(255,255,255), lifetime=Config.hitscan_trail_lifetime, *args, **kwargs):
      Animation.__init__(self, *args, **kwargs)
      self.start = start
      self.end = end
      self.color = color
      self.lifetime = lifetime
      self.age = 0.0
   
   def update(self):
      self.age += self.frame()
      if self.age > self.lifetime:
         return True
      else:
         color = [int(c * (1 - self.age / self.lifetime)) for c in self.color]
         pygame.draw.aaline(self.screen, color, self.start, self.end)
         return False
